﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Dwarf.Managed
{
    public class FiniteStateMachineBaseMethod
    {
        private string pattern;                             // pattern to be found
        private string content;                             // content for processing
        private int[,] states;                              // matrix for machine states storing
        private int stateDimensionCoincidence;              // x-dimension of the states matrix (all chars in the pattern text)
        private static int stateDimensionChars = 65534;     // y-dimension of the states matrix (all possible chars)
        private int initialPosition;                        // constant to be added to all result positions
        private List<int> positions;                        // search result
        private int contentLength = 0;                      // length of content

        /// <summary>Setter for states array</summary>
        /// <param name="pattern">Value of string representing the pattern to be search</param>
        /// <param name="states">Array for content string traverse</param>
        /// <param name="initialPosition">Value of caonstant to be added to all result positions</param>
        /// <param name="content">Value of content string to be traversed</param>
        ///
        public FiniteStateMachineBaseMethod(string pattern, int[,] states, int initialPosition, String content)
        {
            SetPattern(pattern);
            SetStates(states);
            SetInitialPosition(initialPosition);
            SetContent(content);
            positions = new List<int>();
        }

        /// <summary>Fill the states array for content string traverse</summary>
        /// <param name="states">Array to filled</param>
        /// <param name="pattern">Pattern string</param>
        /// 
        public static void ConstructStates(ref int[,] states, string pattern)
        {
            int stateDimensionCoincidence = pattern.Length;
            states = new int[stateDimensionChars, stateDimensionCoincidence + 1];   // init array
            for (int i = 0; i < stateDimensionCoincidence; i++)
            {
                states[pattern[0], i] = 1;  // all turns from any char to the the first char in pattern are "initial" (=1)
            }
            for (int i = 1; i < stateDimensionCoincidence; i++)
            {
                int currentState = states[pattern[i], i];
                if (currentState != 0)      // cycle-in-pattern checker
                {
                    states[pattern[currentState], i + 1] = currentState + 1;    // if cycle found, add "cycle" state
                }
                states[pattern[i], i] = i + 1;  // all turns from i-char to the (i+1)-char are "next" (equals to i+1)
            }
        }

        /// <summary>Setter for states array</summary>
        /// <param name="states">Array for content string traverse</param>
        ///
        public void SetStates(int[,] states)
        {
            this.states = states;
        }

        /// <summary>Setter for pattern</summary>
        /// <param name="pattern">Value of string representing the pattern to be search</param>
        ///
        public void SetPattern(string pattern)
        {
            this.pattern = pattern;
            stateDimensionCoincidence = pattern.Length;
        }

        /// <summary>Setter for initial position</summary>
        /// <param name="initialPosition">Value of caonstant to be added to all result positions</param>
        ///
        public void SetInitialPosition(int initialPosition)
        {
            this.initialPosition = initialPosition;
        }

        /// <summary>Setter for content string</summary>
        /// <param name="content">Value of content string to be traversed</param>
        /// 
        public void SetContent(String content)
        {
            this.content = content;
        }

        /// <summary>Setter for content string</summary>
        /// <param name="contentLength">Value of content string length</param>
        /// 
        public void SetContentLength(int contentLength)
        {
            this.contentLength = contentLength;
        }
        
        /// <summary>Return positions as the result of solving</summary>
        /// <returns>Positions list</returns>
        ///
        public List<int> GetPositionsList()
        {
            return positions;
        }

        /// <summary>Solver for the content</summary>
        ///
        public void Solve()
        {
            if (contentLength == 0)
            {
                SetContentLength(content.Length);
            }
            int state = 0;
            int currentPosition = 0;
            while (currentPosition < contentLength)                // traverse the defined part of content string
            {
                state = states[content[currentPosition], state];    // state is defined by point (2 dimensional) in the states array: combination of current state value and current char in content string
                if (state == stateDimensionCoincidence)
                {
                    positions.Add(initialPosition + currentPosition);// the final state in array. add to the result to common list
                    state = 0;                                      // begin the search in the last part of string
                }
                currentPosition++;                                  // current index in content string (<content.Length)
            }
        }
    }
}
